#include "MainWindow.hpp"
#include "CLMarkWidget.hpp"
#include "../cl/PlatformInfo.hpp"
#include "../cl/DeviceInfo.hpp"
#include <QLabel>
#include <QString>
#include <QStatusBar>
#include <QToolBar>
#include <QComboBox>
#include <QEvent>
#include <iostream>
#include <sstream>
#include <string>

using namespace ui;
using namespace std;

namespace {
    const char *WINDOW_TITLE = QT_TR_NOOP("clMark");
    const char *TOOL_BAR_TITLE = QT_TR_NOOP("Standard");
    const char *PLATFORM = QT_TR_NOOP("Platform");
    const char *PLATFORM_NOT_FOUND = QT_TR_NOOP("No OpenCL Platform found!");
    const char *DEVICE = QT_TR_NOOP("Device");
    const char *DEVICE_NOT_FOUND = QT_TR_NOOP("No OpenCL device found!");
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    toolBar(addToolBar(tr(TOOL_BAR_TITLE))),
    comboBoxPlatforms(new QComboBox()),
    comboBoxDevices(new QComboBox()),
    clMarkWidget(new CLMarkWidget()),
    labelStatus(new QLabel()),
    platformIndex(-1),
    deviceIndex(-1),
    isLocked(false) {
    setWindowTitle(tr(WINDOW_TITLE));
    setCentralWidget(clMarkWidget);
    setupUi();
    setupCl();
}

MainWindow::~MainWindow() { }

void MainWindow::setupUi(void) {
    toolBar->addWidget(new QLabel(QString(" ").append(tr(PLATFORM)).append(" ")));
    toolBar->addWidget(comboBoxPlatforms);
    toolBar->addSeparator();
    toolBar->addWidget(new QLabel(QString(" ").append(tr(DEVICE)).append(" ")));
    toolBar->addWidget(comboBoxDevices);
    toolBar->addSeparator();
    statusBar()->addWidget(labelStatus);
    connect(comboBoxPlatforms, SIGNAL(currentIndexChanged(int)),
            this,              SLOT(onPlatformChanged(int)));
    connect(comboBoxDevices, SIGNAL(currentIndexChanged(int)),
            this,            SLOT(onDeviceChanged(int)));
    connect(clMarkWidget, SIGNAL(isLocked(bool)),
            this,         SLOT(onIsLocked(bool)));
    comboBoxPlatforms->installEventFilter(this);
    comboBoxDevices->installEventFilter(this);
}

void MainWindow::setupCl(void) {
    try {
        cl::Platform::get(&platforms);
    } catch(cl::Error &e) {
        stringstream ss;
        ss << e.what() << "(" << e.err() << ")";
        string err = ss.str();
        labelStatus->setText(
                    tr(PLATFORM_NOT_FOUND).append(" ").append(err.c_str()));
        cerr << tr(PLATFORM_NOT_FOUND).toStdString() << " " << err << endl;
    }
    loadPlatforms();
    if(platforms.size())
        loadDevices(platforms[platformIndex]);
}

void MainWindow::loadPlatforms(void) {
    comboBoxPlatforms->clear();
    for(size_t i=0; i<platforms.size(); i++) {
        cl::PlatformInfo info = cl::PlatformInfo(platforms[i]);
        comboBoxPlatforms->addItem(info.getName());
    }
}

void MainWindow::loadDevices(cl::Platform &platform) {
    comboBoxDevices->clear();
    platform.getDevices(CL_DEVICE_TYPE_ALL, &devices);
    for(size_t i=0; i<devices.size(); i++) {
        cl::DeviceInfo info = cl::DeviceInfo(devices[i]);
        comboBoxDevices->addItem(info.getName());
    }
    QString s;
    cl::PlatformInfo info = cl::PlatformInfo(platform);
    if(devices.size() == 0) {
        s.append(tr(PLATFORM)).append(" '").append(info.getName()).
                append("': ").append(tr(DEVICE_NOT_FOUND));
        labelStatus->setText(s);
        cerr << s.toStdString() << endl;
    } else {
        stringstream ss;
        ss << devices.size();
        s.append(tr(PLATFORM)).append(" '").append(info.getName()).
                append("': ").append(tr("Found ")).append(ss.str().c_str()).
                append(" ").append(tr(DEVICE)).append(tr("(s)."));
        labelStatus->setText(s);
    }
}

void MainWindow::onPlatformChanged(int index) {
    if((index >= 0) && (platformIndex != index)) {
        platformIndex = index;
        if(platforms.size()) {
            clMarkWidget->setPlatform(platforms[platformIndex]);
            deviceIndex = -1;
            loadDevices(platforms[platformIndex]);
        }
    }
}

void MainWindow::onDeviceChanged(int index) {
    if((index >= 0) && (deviceIndex != index)) {
        deviceIndex = index;
        if(devices.size()) {
            clMarkWidget->setDevice(devices[deviceIndex]);
            clMarkWidget->showInfo();
            clMarkWidget->showBenchmark();
        }
    }
}

void MainWindow::onIsLocked(bool result) {
    isLocked = result;
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event) {
    if((event->type() == QEvent::MouseButtonPress) && isLocked) {
        return true;
    } else {
        return QMainWindow::eventFilter(watched, event);
    }
}
